// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_timer.h"

namespace MK3DE
{
	CTimer::CTimer()
	{

	}

	CTimer::~CTimer()
	{

	}

	BOOL CTimer::Init()
	{
		DWORD procMask;
		DWORD sysMask;

		GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);

		if (procMask == 0)
			_timerMask = 1;
		else
		{
			_timerMask = 1;
			while ((_timerMask & procMask) == 0)
				_timerMask <<= 1;
		}

		HANDLE thread = GetCurrentThread();
		DWORD_PTR old = SetThreadAffinityMask(thread, _timerMask);
		QueryPerformanceFrequency(&_frequency);
		QueryPerformanceCounter(&_startTime);
		_startTick = timeGetTime();
		SetThreadAffinityMask(thread, old);
		_lastTime = 0;
		return TRUE;
	}

	DWORD CTimer::GetMicroseconds()
	{
		LARGE_INTEGER curTime;
		HANDLE thread = GetCurrentThread();
		DWORD_PTR old = SetThreadAffinityMask(thread, _timerMask);
		QueryPerformanceCounter(&curTime);
		SetThreadAffinityMask(thread, old);

		LONGLONG newTime = curTime.QuadPart - _startTime.QuadPart;
		unsigned long newTicks = (unsigned long)(1000 * newTime / _frequency.QuadPart);
		unsigned long check = timeGetTime() - _startTick;
		signed long offset = (signed long)(newTicks - check);
		if (offset < -100 || offset > 100)
		{
			LONGLONG adjust = (std::min)(offset * _frequency.QuadPart / 1000, newTime - _lastTime);
			_startTime.QuadPart += adjust;
			newTime -= adjust;
		}

		_lastTime = newTime;

		return (unsigned long)(1000000 * newTime / _frequency.QuadPart);
	}

	DWORD CTimer::GetMilliseconds()
	{
		LARGE_INTEGER curTime;
		HANDLE thread = GetCurrentThread();
		DWORD_PTR old = SetThreadAffinityMask(thread, _timerMask);
		QueryPerformanceCounter(&curTime);
		SetThreadAffinityMask(thread, old);

		LONGLONG newTime = curTime.QuadPart - _startTime.QuadPart;
		unsigned long newTicks = (unsigned long)(1000 * newTime / _frequency.QuadPart);
		unsigned long check = timeGetTime() - _startTick;
		signed long offset = (signed long)(newTicks - check);
		if (offset < -100 || offset > 100)
		{
			LONGLONG adjust = (std::min)(offset * _frequency.QuadPart / 1000, newTime - _lastTime);
			_startTime.QuadPart += adjust;
			newTime -= adjust;

			newTicks = (unsigned long) (1000 * newTime / _frequency.QuadPart);
		}

		_lastTime = newTime;

		return newTicks;
	}
}